package com.igrowing.easyRpc.proxy;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.igrowing.easyRpc.RpcApplication;
import com.igrowing.easyRpc.config.RpcConfig;
import com.igrowing.easyRpc.constant.RpcConstant;
import com.igrowing.easyRpc.loadbalancer.LoadBalancer;
import com.igrowing.easyRpc.loadbalancer.LoadBalancerFactory;
import com.igrowing.easyRpc.model.RpcRequest;
import com.igrowing.easyRpc.model.RpcResponse;
import com.igrowing.easyRpc.model.ServiceMetaInfo;
import com.igrowing.easyRpc.protocol.ProtocolKeys;
import com.igrowing.easyRpc.registry.Registry;
import com.igrowing.easyRpc.registry.RegistryFactory;
import com.igrowing.easyRpc.retry.RetryStrategy;
import com.igrowing.easyRpc.retry.RetryStrategyFactory;
import com.igrowing.easyRpc.serializer.JdkSerializer;
import com.igrowing.easyRpc.serializer.Serializer;
import com.igrowing.easyRpc.serializer.SerializerFactory;
import com.igrowing.easyRpc.server.tcp.VertxTcpClient;
import com.igrowing.easyRpc.tolerant.TolerantStrategy;
import com.igrowing.easyRpc.tolerant.TolerantStrategyFactory;

import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ServiceProxy implements InvocationHandler {

    /**
     * 调用代理
     *
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 指定序列化器
        Serializer serializer =  SerializerFactory.getInstance(RpcApplication.getRpcConfig().getSerializer());

        // 构造请求
        String serviceName = method.getDeclaringClass().getName();

        // 构造请求
        RpcRequest rpcRequest = RpcRequest.builder()
                .serviceName(method.getDeclaringClass().getName())
                .methodName(method.getName())
                .parameterTypes(method.getParameterTypes())
                .args(args)
                .build();
        try {
            // 序列化
            byte[] bodyBytes = serializer.serialize(rpcRequest);
            // 从注册中心获取服务提供者请求地址
            RpcConfig rpcConfig = RpcApplication.getRpcConfig();
            Registry registry = RegistryFactory.getInstance(rpcConfig.getRegistryConfig().getRegistry());
            ServiceMetaInfo serviceMetaInfo = new ServiceMetaInfo();
            serviceMetaInfo.setServiceName(serviceName);
            serviceMetaInfo.setServiceVersion(RpcConstant.DEFAULT_SERVICE_VERSION);
            List<ServiceMetaInfo> serviceMetaInfoList = registry.serviceDiscovery(serviceMetaInfo.getServiceKey());
            if (CollUtil.isEmpty(serviceMetaInfoList)) {
                throw new RuntimeException("暂无服务地址");
            }

            // todo 负载均衡
            LoadBalancer loadBalancer = LoadBalancerFactory.getInstance(rpcConfig.getLoadBalancer());
            // todo 将方法调用名作为参数
            Map<String,Object> requestParamMap = new HashMap<>();
            requestParamMap.put("methodName",rpcRequest.getMethodName());
            requestParamMap.put("serviceList",serviceMetaInfoList);
            requestParamMap.put("rpcRequest",rpcRequest);
            ServiceMetaInfo serviceMetaInfo1 = loadBalancer.select(requestParamMap, serviceMetaInfoList);
            requestParamMap.put("serviceMetaInfo",serviceMetaInfo1);

            // 发送请求
            // todo 注意，这里地址被硬编码了（需要使用注册中心和服务发现机制解决）
            if(ProtocolKeys.HTTP_PROTOCOL.equals(rpcConfig.getProtocol()))
            {
                RpcResponse rpcResponse = null;
                try {
                    // todo 可以提取为一个方法
                    RetryStrategy retryStrategy = RetryStrategyFactory.getInstance(rpcConfig.getRetryStrategy());
                    HttpResponse httpResponse = retryStrategy.doRetry(()->HttpRequest.post(serviceMetaInfo1.getServiceAddress()).body(bodyBytes).execute());
//                try (HttpResponse httpResponse = HttpRequest.post(serviceMetaInfo1.getServiceAddress())
//                        .body(bodyBytes)
//                        .execute()) {
//                    byte[] result = httpResponse.bodyBytes();
//                    // 反序列化
//                    RpcResponse rpcResponse = serializer.deserialize(result, RpcResponse.class);
//                    return rpcResponse.getData();
//                }
                    byte[] result = httpResponse.bodyBytes();
                    rpcResponse = serializer.deserialize(result, RpcResponse.class);
                } catch (Exception e) {
                    // todo 容错机制
                    TolerantStrategy tolerantStrategy = TolerantStrategyFactory.getInstance(rpcConfig.getTolerantStrategy());
                    rpcResponse=tolerantStrategy.doTolerant(requestParamMap,e);
                }
                return rpcResponse.getData();
            }
            else {
                RpcResponse rpcResponse = null;
                try {
                    RetryStrategy retryStrategy = RetryStrategyFactory.getInstance(rpcConfig.getRetryStrategy());
                    rpcResponse = retryStrategy.doRetry(() ->
                            VertxTcpClient.doRequest(rpcRequest, serviceMetaInfo1)
                    );
                } catch (Exception e) {
                    //todo 容错机制
                    TolerantStrategy tolerantStrategy = TolerantStrategyFactory.getInstance(rpcConfig.getTolerantStrategy());
                    rpcResponse=tolerantStrategy.doTolerant(requestParamMap,e);
                }
                return rpcResponse.getData();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }
}